﻿using System;
using System.Runtime.InteropServices;
using System.Text;

using WealthLab;

namespace QuikApi
{
    //Класс содержит имя клиента и номер счета
    public class ClientParams
    {
        private string account;
        private string client;

        public ClientParams(string account, string client)
        {
            this.account = account;
            this.client = client;
        }

        public string Account
        {
            get { return account; }
            set { account = value; }
        }

        public string Client
        {
            get { return client; }
            set { client = value; }
        }
    }

    //Обертка для WL
    public static class QuikSignalWraper
    {
        private static ClientParams client = new ClientParams("L01-00000F00", "Zhurikhin Denis");

        /// <summary>
        /// Перед тем как отправить заявку в Quik проверяем соеденение
        /// </summary>
        /// <returns></returns>
        private static String SetConnection()
        {
            String errMsg = null;

            if (QuikTransApiWraper.is_dll_connected() != null)
            {
                //Dll не подсоединена, пытаемся подсоединить
                errMsg = QuikTransApiWraper.connect();

                if (errMsg != null)
                {
                    //проблемы с соединением Dll и Quik
                    return errMsg;
                }
            }

            //вторая проверка, соединен ли терминал с сервером
            return QuikTransApiWraper.is_quik_connected();
        }

        /// <summary>
        /// Проверяем валидность входных параметров
        /// </summary>
        /// <param name="bars"></param>
        /// <param name="barNumber"></param>
        /// <returns></returns>
        private static Boolean isArgumentsValid(Bars bars, int barNumber)
        {
            if (bars == null || barNumber < 0 || (bars.Count - 1) < barNumber)
            {
                return false;
            }
            else
            {
                return true;
            }

        }

        /// <summary>
        /// Проверка заявки на Back Dating
        /// Допускается задержка на 5 минут или разница во времени с терминалом в 5 минут
        /// </summary>
        /// <returns></returns>
        private static Boolean isBackDate(Bars bars, int barNumber) 
        {
            return bars.Date[barNumber].AddMinutes(5).CompareTo(DateTime.Now) < 0;
        }


        /// <summary>
        /// Формируем строку для запроса
        /// </summary>
        /// <param name="bar"></param>
        /// <param name="barNumber"></param>
        /// <param name="symbolInfo"></param>
        /// <returns></returns>
        private static String createTransactionString(Bars bars, int barNumber, String symbolInfo, String direction, String orderType, double limitPrice)
        {
            double priceStr = 0;

            //определяем цену
            if (orderType == "MO") 
            {
                //Market Open Bar Price
                priceStr = Math.Round(bars.Open[barNumber], 2); 

                orderType = "L";
            }
            else if (orderType == "MC") 
            {
                //Market Close Bar Price
                priceStr = Math.Round(bars.Close[barNumber], 2); 

                orderType = "L";
            }
            else if (orderType == "L")
            {
                //Limit Price
                priceStr = Math.Round(limitPrice, 2); 
            }
            else
            {
                throw new ArgumentException("Unknown order type: " + orderType);
            }

            //уникальный номер транзакции (http://quik.ru/forum/qpile/62871/63059/#m63059)
            string transactionId = DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString();

            //класс бумаги
            String secClass = symbolInfo.Substring(0, symbolInfo.IndexOf('.'));

            //название бумаги
            String secName = symbolInfo.Substring(symbolInfo.IndexOf('.') + 1, symbolInfo.Length - symbolInfo.IndexOf('.') - 1);

            //формируем заявку для Quik
            string TransactionString = "ACCOUNT=" + client.Account +
                "; ACTION=NEW_ORDER; TYPE=" + orderType+ "; TRANS_ID=" + transactionId + "; CLASSCODE=" + secClass +
                "; SECCODE=" + secName + "; OPERATION=" + direction + "; PRICE=" + priceStr + "; QUANTITY=1; ";

            return TransactionString;
        }

        #region AtMarket сигналы

        /// <summary>
        /// Реализация BuyAtMarket
        /// </summary>
        public static String BuyAtMarket(Bars bars, int barNumber, String symbolInfo)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "B", "MO", 0); 

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }

        /// <summary>
        /// Реализация BuyAtMarket
        /// </summary>
        public static String Test(double price, String symbolInfo)
        {
            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            double priceStr = 0;

            string orderType = "MO";

            //определяем цену
            if (orderType == "MO")
            {
                priceStr = Math.Round(price, 2);

                orderType = "L";
            }
            else if (orderType == "MC")
            {
                priceStr = Math.Round(price, 2);

                orderType = "L";
            }
            else if (orderType == "L")
            {
                priceStr = Math.Round(price, 2);
            }
            else
            {
                throw new ArgumentException("Unknown order type: " + orderType);
            }

            //уникальный номер транзакции (http://quik.ru/forum/qpile/62871/63059/#m63059)
            string transactionId = DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString();

            //класс бумаги
            String secClass = symbolInfo.Substring(0, symbolInfo.IndexOf('.'));

            //название бумаги
            String secName = symbolInfo.Substring(symbolInfo.IndexOf('.') + 1, symbolInfo.Length - symbolInfo.IndexOf('.') - 1);

            //формируем заявку для Quik
            string TransactionString = "ACCOUNT=" + client.Account +
                "; ACTION=NEW_ORDER; TYPE=" + orderType + "; TRANS_ID=" + transactionId + "; CLASSCODE=" + secClass +
                "; SECCODE=" + secName + "; OPERATION=" + "B" + "; PRICE=" + priceStr + "; QUANTITY=1; ";

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }

        /// <summary>
        /// Реализация ShortAtMarket. Открытие Short по рынку
        /// </summary>
        public static String ShortAtMarket(Bars bars, int barNumber, String symbolInfo)
        {
            return SellAtMarket(bars, barNumber, symbolInfo);
        }

        /// <summary>
        /// Реализация CoverAtMarket. Закрытие Short по рынку
        /// </summary>
        public static String CoverAtMarket(Bars bars, int barNumber, String symbolInfo)
        {
            return BuyAtMarket(bars, barNumber, symbolInfo);
        }


        /// <summary>
        /// Реализация универсального ExitAtMarket. Закрытие Short or Long по рынку
        /// </summary>
        public static String ExitAtMarket(Bars bars, int barNumber, String symbolInfo, Position position)
        {
            if (position.PositionType == PositionType.Long)
            {
                return SellAtMarket(bars, barNumber, symbolInfo);
            }
            else
            {
                return CoverAtMarket(bars, barNumber, symbolInfo);
            }
        }

        /// <summary>
        /// Реализация SellAtMarket
        /// </summary>
        public static String SellAtMarket(Bars bars, int barNumber, String symbolInfo)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "S", "MO", 0); 

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }


        #endregion AtMarket

        #region AtLimit сигналы

        /// <summary>
        /// Реализация BuyAtLimit
        /// </summary>
        public static String BuyAtLimit(Bars bars, int barNumber, String symbolInfo, double limitPrice)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "B", "L", limitPrice);

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }


        /// <summary>
        /// Реализация SellAtLimit
        /// </summary>
        public static String SellAtLimit(Bars bars, int barNumber, String symbolInfo, double limitPrice)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "S", "L", limitPrice);

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }

        /// <summary>
        /// Реализация ShortAtLimit. Открытие Short c лимитом
        /// </summary>
        public static String ShortAtLimit(Bars bars, int barNumber, String symbolInfo, double limitPrice)
        {
            return SellAtLimit(bars, barNumber, symbolInfo, limitPrice);
        }

        /// <summary>
        /// Реализация CoverAtLimit. Закрытие Short c лимитом
        /// </summary>
        public static String CoverAtLimit(Bars bars, int barNumber, String symbolInfo, double limitPrice)
        {
            return BuyAtLimit(bars, barNumber, symbolInfo, limitPrice);
        }


        /// <summary>
        /// Реализация универсального ExitAtLimit. Закрытие Short or Long с лимитом
        /// </summary>
        public static String ExitAtLimit(Bars bars, int barNumber, String symbolInfo, Position position, double limitPrice)
        {
            if (position.PositionType == PositionType.Long)
            {
                return SellAtLimit(bars, barNumber, symbolInfo, limitPrice);
            }
            else
            {
                return CoverAtLimit(bars, barNumber, symbolInfo, limitPrice);
            }
        }

        #endregion


        #region AtClose сигналы

        /// <summary>
        /// Реализация BuyAtClose
        /// </summary>
        public static String BuyAtClose(Bars bars, int barNumber, String symbolInfo)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "B", "MC", 0);

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }


        /// <summary>
        /// Реализация SellAtClose
        /// </summary>
        public static String SellAtClose(Bars bars, int barNumber, String symbolInfo)
        {
            if (!isArgumentsValid(bars, barNumber))
            {
                //ошибка с входными параметрами
                return "ARGUMENTS_ERROR";
            }

            if (isBackDate(bars, barNumber))
            {
                //нет необходимости отправки заявки, если это история, просто выходим
                return null;
            }

            if (barNumber != (bars.Count - 1))
            {
                //отправляем синал только для последнего бара
                return null;
            }

            //устанавливаем соединение
            String errMsg = SetConnection();
            if (errMsg != null)
            {
                //информируем об ошибке
                return errMsg;
            }

            //номер ордера
            double OrderNum = 0;

            //формируем заявку для Quik
            string TransactionString = createTransactionString(bars, barNumber, symbolInfo, "S", "MC", 0);

            //отправляем заявку в Quik
            return QuikTransApiWraper.send_sync_transaction(TransactionString, ref OrderNum);

        }

        /// <summary>
        /// Реализация ShortAtClose. Открытие Short c лимитом
        /// </summary>
        public static String ShortAtClose(Bars bars, int barNumber, String symbolInfo)
        {
            return SellAtClose(bars, barNumber, symbolInfo);
        }

        /// <summary>
        /// Реализация CoverAtClose. Закрытие Short c лимитом
        /// </summary>
        public static String CoverAtClose(Bars bars, int barNumber, String symbolInfo)
        {
            return BuyAtClose(bars, barNumber, symbolInfo);
        }


        /// <summary>
        /// Реализация универсального ExitAtLimit. Закрытие Short or Long с лимитом
        /// </summary>
        public static String ExitAtClose(Bars bars, int barNumber, String symbolInfo, Position position)
        {
            if (position.PositionType == PositionType.Long)
            {
                return SellAtClose(bars, barNumber, symbolInfo);
            }
            else
            {
                return CoverAtClose(bars, barNumber, symbolInfo);
            }
        }

        #endregion

    }


    //Статический класс для вызова функций библиотеки Trans2Quik.dll
    public static class QuikTransApiWraper
    {
        #region Константы возвращаемых значений

        public const long TRANS2QUIK_SUCCESS = 0;
        public const long TRANS2QUIK_FAILED = 1;
        public const long TRANS2QUIK_QUIK_TERMINAL_NOT_FOUND = 2;
        public const long TRANS2QUIK_DLL_VERSION_NOT_SUPPORTED = 3;
        public const long TRANS2QUIK_ALREADY_CONNECTED_TO_QUIK = 4;
        public const long TRANS2QUIK_WRONG_SYNTAX = 5;
        public const long TRANS2QUIK_QUIK_NOT_CONNECTED = 6;
        public const long TRANS2QUIK_DLL_NOT_CONNECTED = 7;
        public const long TRANS2QUIK_QUIK_CONNECTED = 8;
        public const long TRANS2QUIK_QUIK_DISCONNECTED = 9;
        public const long TRANS2QUIK_DLL_CONNECTED = 10;
        public const long TRANS2QUIK_DLL_DISCONNECTED = 11;
        public const long TRANS2QUIK_MEMORY_ALLOCATION_ERROR = 12;
        public const long TRANS2QUIK_WRONG_CONNECTION_HANDLE = 13;
        public const long TRANS2QUIK_WRONG_INPUT_PARAMS = 14;

        #endregion

        public static string ResultToString(long Result)
        {
            switch (Result)
            {
                case TRANS2QUIK_SUCCESS:                                //0
                    return "TRANS2QUIK_SUCCESS";
                case TRANS2QUIK_FAILED:                                 //1
                    return "TRANS2QUIK_FAILED";
                case TRANS2QUIK_QUIK_TERMINAL_NOT_FOUND:                //2
                    return "TRANS2QUIK_QUIK_TERMINAL_NOT_FOUND";
                case TRANS2QUIK_DLL_VERSION_NOT_SUPPORTED:              //3
                    return "TRANS2QUIK_DLL_VERSION_NOT_SUPPORTED";
                case TRANS2QUIK_ALREADY_CONNECTED_TO_QUIK:              //4
                    return "TRANS2QUIK_ALREADY_CONNECTED_TO_QUIK";
                case TRANS2QUIK_WRONG_SYNTAX:                           //5
                    return "TRANS2QUIK_WRONG_SYNTAX";
                case TRANS2QUIK_QUIK_NOT_CONNECTED:                     //6
                    return "TRANS2QUIK_QUIK_NOT_CONNECTED";
                case TRANS2QUIK_DLL_NOT_CONNECTED:                      //7
                    return "TRANS2QUIK_DLL_NOT_CONNECTED";
                case TRANS2QUIK_QUIK_CONNECTED:                         //8
                    return "TRANS2QUIK_QUIK_CONNECTED";
                case TRANS2QUIK_QUIK_DISCONNECTED:                      //9
                    return "TRANS2QUIK_QUIK_DISCONNECTED";
                case TRANS2QUIK_DLL_CONNECTED:                          //10
                    return "TRANS2QUIK_DLL_CONNECTED";
                case TRANS2QUIK_DLL_DISCONNECTED:                       //11
                    return "TRANS2QUIK_DLL_DISCONNECTED";
                case TRANS2QUIK_MEMORY_ALLOCATION_ERROR:                //12
                    return "TRANS2QUIK_MEMORY_ALLOCATION_ERROR";
                case TRANS2QUIK_WRONG_CONNECTION_HANDLE:                //13
                    return "TRANS2QUIK_WRONG_CONNECTION_HANDLE";
                case TRANS2QUIK_WRONG_INPUT_PARAMS:                     //14
                    return "TRANS2QUIK_WRONG_INPUT_PARAMS";
                default:
                    return "UNKNOWN_VALUE";
            }
        }

        public static string ByteToString(byte[] Str)
        {
            string s = "";
            for (int i = 0; i < Str.Length; i++)
            {
                s = s + Str[i].ToString();
            }
            return s;
        }

        #region Функция для установления связи библиотеки TRANS2QUIK.dll с терминалом QUIK
        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_CONNECT@16", CallingConvention = CallingConvention.StdCall)]
        static extern long connect(string lpcstrConnectionParamsString, ref long pnExtendedErrorCode, byte[] lpstrErrorMessage, UInt32 dwErrorMessageSize);
        public static String connect()
        {
            string path = @"C:\FinamJunior\";
            Byte[] EMsg = new Byte[250];
            long ExtEC = 0, result = -1;

            result = connect(path, ref ExtEC, EMsg, (UInt32) EMsg.Length) & 255;

            if (result == TRANS2QUIK_SUCCESS)
            {
                return null;
            }
            else
            {
                return System.Text.Encoding.Default.GetString(EMsg);
            }
        }

        #endregion

        #region Функция используется для проверки наличия соединения между библиотекой TRANS2QUIK.dll и терминалом QUIK
        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_IS_DLL_CONNECTED@12",
            CallingConvention = CallingConvention.StdCall)]
        static extern long is_dll_connected(ref long pnExtendedErrorCode, byte[] lpstrErrorMessage, UInt32 dwErrorMessageSize);
        public static String is_dll_connected()
        {
            Byte[] EMsg = new Byte[250];
            long ExtEC = 0, result = -1;

            result = is_dll_connected(ref ExtEC, EMsg, (UInt32) EMsg.Length) & 255;

            if (result == TRANS2QUIK_DLL_CONNECTED)
            {
                return null;
            }
            else
            {
                return System.Text.Encoding.Default.GetString(EMsg);
            }

        }

        #endregion

        #region Функция используется для проверки наличия соединения между терминалом QUIK и сервером
        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_IS_QUIK_CONNECTED@12",
            CallingConvention = CallingConvention.StdCall)]
        static extern long is_quik_connected(ref long pnExtendedErrorCode, byte[] lpstrErrorMessage, UInt32 dwErrorMessageSize);
        public static String is_quik_connected()
        {
            Byte[] EMsg = new Byte[250];
            long ExtEC = 0, result = -1;

            result = is_quik_connected(ref ExtEC, EMsg, (UInt32) EMsg.Length) & 255;

            if (result == TRANS2QUIK_QUIK_CONNECTED)
            {
                return null;
            }
            else
            {
                return System.Text.Encoding.Default.GetString(EMsg);
            }
        }
        #endregion

        #region Функция используется для разрыва связи библиотеки TRANS2QUIK.dll с терминалом QUIK
        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_DISCONNECT@12",
          CallingConvention = CallingConvention.StdCall)]
        static extern long disconnect(ref long pnExtendedErrorCode, byte[] lpstrErrorMessage, UInt32 dwErrorMessageSize);
        public static Boolean disconnect_test(bool FinalPause)
        {
            Byte[] dEMsg = new Byte[50];
            long dExtEC = 0, result = -1;

            result = disconnect(ref dExtEC, dEMsg, (UInt32) dEMsg.Length) & 255;

            if (result == TRANS2QUIK_SUCCESS || result == TRANS2QUIK_DLL_NOT_CONNECTED)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region Отправка заявки синхронно до получения ответа от Quik
        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_SEND_SYNC_TRANSACTION@36",
            CallingConvention = CallingConvention.StdCall)]
        static extern long send_sync_transaction(
            string lpstTransactionString,
            ref long pnReplyCode,
            ref int pdwTransId,
            ref double pdOrderNum,
            byte[] lpstrResultMessage,
            UInt32 dwResultMessageSize,
            ref long pnExtendedErrorCode,
            byte[] lpstrErrorMessage,
            UInt32 dwErrorMessageSize);
        public static String send_sync_transaction(string transactionStr, ref double OrderNum)
        {
            Byte[] EMsg = new Byte[250];
            Byte[] ResMsg = new Byte[250];
            long ExtEC = -100, result = -1, ReplyCd = 0;
            int TransID = 0;

            result = send_sync_transaction(transactionStr, ref ReplyCd, ref TransID, ref OrderNum,
                 ResMsg, (UInt32)ResMsg.Length, ref ExtEC, EMsg, (UInt32) EMsg.Length) & 255;

            if (result == TRANS2QUIK_SUCCESS)
            {
                if (ReplyCd != 3)
                {
                    return System.Text.Encoding.Default.GetString(ResMsg);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return System.Text.Encoding.Default.GetString(ResMsg);
            }

        }

        #endregion

        #region connection_status_callback
        public delegate void connection_status_callback(UInt32 nConnectionEvent, UInt32 nExtendedErrorCode, byte[] lpstrInfoMessage);

        [DllImport("TRANS2QUIK.DLL", EntryPoint = "_TRANS2QUIK_SET_CONNECTION_STATUS_CALLBACK@16", CallingConvention = CallingConvention.StdCall)]
        public static extern long set_connection_status_callback(connection_status_callback pfConnectionStatusCallback, UInt32 pnExtendedErrorCode, byte[] lpstrErrorMessage, UInt32 dwErrorMessageSize);
        public static void connection_status_callback_Report(UInt32 nConnectionEvent, UInt32 nExtendedErrorCode, byte[] lpstrInfoMessage)
        {
            Console.WriteLine("--------------------------------------------------" +
                "\n\t<<<<connection_status_callback_Report>>>>");

            Console.WriteLine("  nConnectionEvent:\t{0} {1}\n  nExtendedErrorCode:\t{2}\n  dwErrorMessageSize:\t{3}",
                nConnectionEvent, ResultToString(nConnectionEvent), (nExtendedErrorCode & 255), lpstrInfoMessage);

            Console.WriteLine("--------------------------------------------------\n");
        }

        #endregion

    }
}
